import { h, Component } from 'preact';

import { add, destory, envoke } from "@store";
import { getCursor, getNewStyle, getLength, degToRadian } from "./utils";
import "@static:css/components/dnd/index.less";

const zoomableMap = {
  'n': 't',
  's': 'b',
  'e': 'r',
  'w': 'l',
  'ne': 'tr',
  'nw': 'tl',
  'se': 'br',
  'sw': 'bl'
}

let onlyEventOne = false;
window.selectedId = '';

export default class RectDND extends Component {
  
  limitWidth = 1000;
  limitHeight = 2000;

  state = {
    left: 0,
    top: 0,
    width: 300,
    height: 200,
    zoomable: 'n, w, s, e, nw, ne, se, sw',
    hasSelected: false
  }

  setElementRef = (ref) => { this.$element = ref }

  // Drag
  startDrag = (e) => {
    e.stopImmediatePropagation();
    const { onDragStart = null, uuid = '', onDrag = null, onDragEnd = null} = this.props;
    const compName = `${uuid}-chart`;
    let { clientX: startX, clientY: startY } = e
    onDragStart && onDragStart()
    this._isMouseDown = true;

    if(selectedId !== compName){
      if(selectedId){
        envoke(selectedId, 'clearSelected', {});
      }
      selectedId = compName;
    }
    this.setState({
      hasSelected: true
    })
    const onMove = (e) => {
      if (!this._isMouseDown) return // patch: fix windows press win key during mouseup issue
      e.stopImmediatePropagation()
      const { clientX, clientY } = e
      const deltaX = clientX - startX
      const deltaY = clientY - startY
      onDrag && onDrag(deltaX, deltaY)
      startX = clientX
      startY = clientY
      let { left, top, width, height } = this.state;
      left += deltaX;
      top += deltaY;
      this.setState({
        left,
        top
      });
      envoke(`${uuid}-rect`, 'updateRect', {left, top, width, height});
    }
    const onUp = () => {
      document.removeEventListener('mousemove', onMove)
      document.removeEventListener('mouseup', onUp)
      if (!this._isMouseDown) return
      this._isMouseDown = false;
      onDragEnd && onDragEnd()
    }
    document.addEventListener('mousemove', onMove)
    document.addEventListener('mouseup', onUp)
  }

  handleResize = (length, alpha, rect, type, isShiftKey) => {
    const { onResize = null } = this.props;
    if (!onResize) return;
    const rotateAngle = 0;
    const aspectRatio = 0;
    const minWidth = -Infinity;
    const minHeight = -Infinity;
    const parentRotateAngle = 0;

    const beta = alpha - degToRadian(rotateAngle + parentRotateAngle)
    const deltaW = length * Math.cos(beta)
    const deltaH = length * Math.sin(beta)
    const ratio = isShiftKey && !aspectRatio ? rect.width / rect.height : aspectRatio;
    const {
      position: { centerX, centerY },
      size: { width, height }
    } = getNewStyle(type, { ...rect, rotateAngle }, deltaW, deltaH, ratio, minWidth, minHeight)
    return { width, height }
  }

  // Resize
  startResize = (e, cursor) => {
    if (e.button !== 0) return;
    document.body.style.cursor = cursor;
    const { onResizeStart = null, uuid = '', onResize = null, onResizeEnd = null} = this.props;
    const { left , top, width, height } = this.state;
    const { clientX: startX, clientY: startY } = e
    const centerX = left + width / 2;
    const centerY = top + height / 2;
    const rotateAngle = 0;
    const rect = { width, height, centerX, centerY, rotateAngle }
    const type = e.target.getAttribute('class').split(' ')[ 0 ]
    onResizeStart && onResizeStart();
    this._isMouseDown = true
    const onMove = (e) => {
      if (!this._isMouseDown) return // patch: fix windows press win key during mouseup issue
      e.stopImmediatePropagation()
      const { clientX, clientY } = e
      const deltaX = clientX - startX
      const deltaY = clientY - startY
      const alpha = Math.atan2(deltaY, deltaX)
      const length = getLength(deltaX, deltaY)
      const isShiftKey = e.shiftKey
      const { width, height } = this.handleResize(length, alpha, rect, type, isShiftKey);
      this.setState({
        width,
        height
      });
      onResize && onResize(width, height);
      envoke(`${uuid}-rect`, 'updateRect', {left , top, width, height});
    }

    const onUp = () => {
      document.body.style.cursor = 'auto'
      document.removeEventListener('mousemove', onMove)
      document.removeEventListener('mouseup', onUp)
      if (!this._isMouseDown) return
      this._isMouseDown = false
      onResizeEnd && onResizeEnd()
    }
    document.addEventListener('mousemove', onMove)
    document.addEventListener('mouseup', onUp)
  }

  componentDidMount(){
    var editContainer = document.getElementById('editor-container');
    this.limitWidth = editContainer.clientWidth;
    this.limitHeight = editContainer.clientHeight;
    const { uuid = '', onResize = null } = this.props;
    const compName = `${uuid}-chart`;
    add(compName, "onResize", opts => {
      const {
        width,
        height,
        left,
        top
      } = opts;
      this.setState({
        width,
        height,
        left,
        top
      })
      onResize && onResize(width, height)
    })

    add(compName, "clearSelected", () => {
        this.setState({
          hasSelected: false
        })
    })

    if(!onlyEventOne){
      onlyEventOne = true;

      document.addEventListener('mousedown', () => {
        if(selectedId){
          envoke(selectedId, 'clearSelected', {});
          selectedId = '';
        }
      }, false);
    }
  }

  componentWillUnmount(){
    const { uuid } = this.props;
    destory(`${uuid}-chart`);
}

  render(){
    let {
      left,
      top,
      width,
      height,
      zoomable,
      hasSelected
    } = this.state;

    left = left + width > this.limitWidth ? this.limitWidth - width : left;
    top = top + height > this.limitHeight ? this.limitHeight - height : top;
    const style = {
      width: Math.abs(width),
      height: Math.abs(height),
      left: left < 0 ? 0 : left,
      top: top < 0 ? 0 : top
    }

    const direction = zoomable.split(',').map(d => d.trim()).filter(d => d) // TODO: may be speed up
    const cls = ["rect", "single-resizer"];
    if(hasSelected){
      cls.push("selected");
    }
    return (
      <div
        ref={this.setElementRef}
        onMouseDown={this.startDrag}
        className={cls.join(" ")}
        style={style}
      >
        {this.props.children}

        {
          hasSelected && direction.map(d => {
            const cursor = `${getCursor(0, d)}-resize`
            return (
              <div key={d} style={{ cursor }} className={`${zoomableMap[ d ]} resizable-handler`} onMouseDown={(e) => this.startResize(e, cursor)} />
            )
          })
        }

        {
          hasSelected && direction.map(d => {
            return (
              <div key={d} className={`${zoomableMap[ d ]} square`} />
            )
          })
        }
      </div>
    )
  }
}
